//  Copyright (c) Microsoft Corporation.  All Rights Reserved.

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

using System.IdentityModel.Selectors;
using System.IdentityModel.Claims;

namespace EAI.SAML
{
    public class EAISamlAttribute
    {
        string name;
        string nameFormat;
        string friendlyName;
        readonly ImmutableCollection<string> attributeValues = new ImmutableCollection<string>();

        bool isReadOnly = false;

        public EAISamlAttribute()
        {
        }

        public EAISamlAttribute(XmlReader reader, EAISamlSerializer serializer, SecurityTokenSerializer keyInfoSerializer, SecurityTokenResolver outOfBandTokenResolver)
        {
            this.ReadXml(reader, serializer, keyInfoSerializer, outOfBandTokenResolver);
        }

        public string Name
        {
            get { return this.name; }
            set
            {
                if (isReadOnly)
                    throw new InvalidOperationException("The object is read-only.");

                if (String.IsNullOrEmpty(value))
                    throw new ArgumentException("value");

                this.name = value;
            }
        }

        public string NameFormat
        {
            get { return this.nameFormat; }
            set
            {
                if (isReadOnly)
                    throw new InvalidOperationException("The object is read-only.");

                this.nameFormat = value;
            }
        }

        public string FriendlyName
        {
            get { return this.friendlyName; }
            set
            {
                if (isReadOnly)
                    throw new InvalidOperationException("The object is read-only.");

                this.friendlyName = value;
            }
        }

        public IList<string> AttributeValues
        {
            get 
            {
                return this.attributeValues; 
            }
        }

        void CheckObjectValidity()
        {
            if (String.IsNullOrEmpty(this.name))
                throw new InvalidOperationException("saml:Attribute requires a name to be specified.");
        }

        public void MakeReadOnly()
        {
            if (!this.isReadOnly)
            {
                this.attributeValues.MakeReadOnly();
                this.isReadOnly = true;
            }
        }

        public virtual void AddClaimsToList(List<Claim> claimList)
        {
            if (claimList == null)
                throw new ArgumentNullException("claimList");

            string claimType;
            if (!String.IsNullOrEmpty(nameFormat))
                claimType = String.Format("{0}/{1}", this.nameFormat, this.name);
            else
                claimType = this.name;

            foreach (string attrValue in this.attributeValues)
            {
                claimList.Add(new Claim(claimType, attrValue, Rights.PossessProperty));
            }
        }

        public virtual void ReadXml(XmlReader reader, EAISamlSerializer serializer, SecurityTokenSerializer keyInfoSerializer, SecurityTokenResolver outOfBandTokenResolver)
        {
            if (reader == null)
                throw new ArgumentNullException("reader");

            reader.MoveToContent();
            this.name = reader.GetAttribute(EAISamlConstants.NameAttribute);
            if (String.IsNullOrEmpty(this.name))
                throw new SecurityTokenException("<saml:Attribute> element does not have a valid 'Name' attribute.");

            this.nameFormat = reader.GetAttribute(EAISamlConstants.NameFormatAttribute);
            this.friendlyName = reader.GetAttribute(EAISamlConstants.FriendlyNameAttribute);

            reader.Read();

            while (reader.IsStartElement())
            {
                if (reader.IsStartElement(EAISamlConstants.AttributeValue, EAISamlConstants.Namespace))
                {
                    this.attributeValues.Add(reader.ReadString());
                    reader.ReadEndElement();
                }
                else
                    throw new SecurityTokenException("<saml:Attribute> has unrecognized child elements.");
            }

            if (this.attributeValues == null || this.attributeValues.Count == 0)
                throw new SecurityTokenException("<saml:Attribute> elment requires at least one <saml:AttributeValue> specified as the child element.");

            reader.ReadEndElement();
        }

        public virtual void WriteXml(XmlWriter writer, SecurityTokenSerializer tokenSerializer)
        {
            CheckObjectValidity();

            if (writer == null)
                throw new ArgumentNullException("writer");

            writer.WriteStartElement(EAISamlConstants.Attribute, EAISamlConstants.Namespace);
            writer.WriteAttributeString(EAISamlConstants.NameAttribute, this.name);
            if (!String.IsNullOrEmpty(this.nameFormat))
                writer.WriteAttributeString(EAISamlConstants.NameFormatAttribute, this.nameFormat);
            if (!String.IsNullOrEmpty(this.friendlyName))
                writer.WriteAttributeString(EAISamlConstants.FriendlyNameAttribute, this.friendlyName);

            for (int i = 0; i < this.attributeValues.Count; ++i)
            {
                writer.WriteElementString(EAISamlConstants.AttributeValue, EAISamlConstants.Namespace, this.attributeValues[i]);
            }

            writer.WriteEndElement();
        }
    }
}
